#include <stdio.h>
#if defined(TEST_TARGET_ini)
#include <varch/command.h>
#include <varch/unitt.h>
#include <varch/ini.h>
#else  
#include "init.h"
#include "command.h"
#include "unitt.h"
#include "kern.h"
#include "ini.h"
#endif

/************************************************************************************/
/************************************* Unit Test ************************************/
/************************************************************************************/

// #define EXIT_TEST
extern uint64_t unitt_clock(void);

static int test_0(void)
{
    for (int i = 0; i < 100; i++)
    {
        if (0) 
        {
            
            #if defined (EXIT_TEST)
            exit(0);
            #endif 
            return UNITT_E_FAIL;
        }
    }
    
    return UNITT_E_OK;
}

static void unitt_task(void)
{
    static UNITT_TCASE rand_tests[] = {
        UNITT_TCASE(test_0),
        // UNITT_TCASE(test_1),
        // UNITT_TCASE(test_2),
    };

    static UNITT suites[] = {
        { "xxx suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
    };

    UNITT_EXE(suites);
}

/************************************************************************************/
/************************************* Base Test ************************************/
/************************************************************************************/

#define READ_FILE "test/file/read.ini"
#define WRITE_FILE "test/file/write.ini"

static void ini_preview(ini_t ini)
{
    char *text = NULL;
    text = ini_dumps(ini, 0, NULL);
    if (text)
    {
        printf("%s\r\n", text);
        free(text);
    }
    else  
    {
        printf("[ERROR] dumps fail!!!\r\n");
    }
}

static void test_create(void)
{
    ini_t ini = NULL;
    ini = ini_create();
    if (ini)
    {
        printf("ini_create success!!! %p\r\n", ini);
    }
    ini_delete(ini);
}

static void test_op_sections(void)
{
    ini_t ini = NULL;
    
    ini = ini_create();
    if (ini)
    {
        printf("ini_create success!!! %p\r\n", ini);
    }

    ini_add_section(ini, "Zhang San");
    ini_add_section(ini, "Li Si");
    ini_add_section(ini, "Wang Wu");

    ini_preview(ini);

    printf("=================================\r\n");

    ini_remove_section(ini, "Li Si");

    ini_preview(ini);

    ini_delete(ini);
}

static void test_get_sections(void)
{
    ini_t ini = NULL;
    int count = 0;

    ini = ini_create();
    if (ini)
    {
        printf("ini_create success!!! %p\r\n", ini);
    }

    ini_add_section(ini, "Zhang San");
    ini_add_section(ini, "Li Si");
    ini_add_section(ini, "Wang Wu");

    count = ini_section_count(ini);

    for (int i = 0; i < count; i++)
    {
        printf("ini[%d] = %s\r\n", i, ini_section_name(ini, i));
    }

    printf("section index = %d\r\n", ini_section_index(ini, "Zhang San"));
    printf("section index = %d\r\n", ini_section_index(ini, "Li Si"));
    printf("section index = %d\r\n", ini_section_index(ini, "Wang Wu"));

    ini_delete(ini);
}

static void test_op_pairs(void)
{
    ini_t ini = NULL;

    ini = ini_create();
    if (ini)
    {
        printf("ini_create success!!! %p\r\n", ini);
    }

    ini_set_value(ini, "Zhang San", "age", "18");
    ini_set_value(ini, "Zhang San", "height", "178");
    ini_set_value(ini, "Zhang San", "email", "123456@qq.com");

    ini_set_value(ini, "Li Si", "age", "20");
    ini_set_value(ini, "Li Si", "gender", "man");
    ini_set_value(ini, "Li Si", "weight", "65");

    ini_set_value(ini, "Wang Wu", "age", "22");

    ini_preview(ini);

    printf("=================================\r\n");

    ini_remove_key(ini, "Li Si", "weight");

    ini_preview(ini);

    printf("=================================\r\n");

    printf("ini[Zhang San][height] = %s\r\n", ini_get_value(ini, "Zhang San", "height"));

    ini_delete(ini);
}

static void test_get_key(void)
{
    ini_t ini = NULL;
    int count = 0;

    ini = ini_create();
    if (ini)
    {
        printf("ini_create success!!! %p\r\n", ini);
    }

    ini_set_value(ini, "Zhang San", "age", "18");
    ini_set_value(ini, "Zhang San", "height", "178");
    ini_set_value(ini, "Zhang San", "email", "123456@qq.com");
    ini_set_value(ini, "Li Si", "age", "20");
    ini_set_value(ini, "Li Si", "gender", "man");
    ini_set_value(ini, "Li Si", "weight", "65");
    ini_set_value(ini, "Wang Wu", "age", "22");

    count = ini_pair_count(ini, "Zhang San");

    for (int i = 0; i < count; i++)
    {
        printf("ini[%d] = %s\r\n", i, ini_key_name(ini, "Zhang San", i));
    }

    printf("pair index = %d\r\n", ini_key_index(ini, "Zhang San", "age"));
    printf("pair index = %d\r\n", ini_key_index(ini, "Zhang San", "height"));
    printf("pair index = %d\r\n", ini_key_index(ini, "Zhang San", "email"));

    ini_delete(ini);
}

static void test_dump(void)
{
    ini_t ini = NULL; // 定义ini对象，习惯初始化为NULL

    ini = ini_create(); // 创建空ini对象
    if (ini == NULL) 
    {
        printf("ini create fail!\r\n");
        return;
    } 

    /* 添加section */
    ini_add_section(ini, "Zhang San");
    ini_add_section(ini, "Li Si");
    ini_add_section(ini, "Wang Wu");

    /* 添加键值 */
    ini_set_value(ini, "Zhang San", "age", "18");
    ini_set_value(ini, "Zhang San", "height", "178");
    ini_set_value(ini, "Zhang San", "email", "123456@qq.com");

    ini_set_value(ini, "Li Si", "age", "20");
    ini_set_value(ini, "Li Si", "gender", "man");
    ini_set_value(ini, "Li Si", "weight", "65");

    ini_set_value(ini, "Wang Wu", "age", "22");

    /* 转储ini到文件 */
    ini_file_dump(ini, WRITE_FILE);

    ini_delete(ini); // 用完之后需要删除
}

static void test_load(void) 
{
    ini_t ini;
    char *p = NULL;
    int len = 0;

    ini = ini_file_load(READ_FILE);
    if (!ini)
    {
        int line, type;
        ini_error_info(&line, &type);
        printf("ini parse error! line %d, error %d.\r\n", line, type);
        // printf("load fail!\r\n");
        return;
    }
    printf("load success!\r\n");
    // ini_set_value(ini, "王五", "年级", "2");
    // ini_set_value(ini, "王五", "班级", "1");
    // ini_set_value(ini, "王五", "类别", "理科");
    // printf("%s\r\n", ini_key_name(ini, "王五", 2));
    // 
    #if 1
    for (int i = 0; i < ini_section_count(ini); i++)
    {
        char *s = (const char*)ini_section_name(ini, i);
        printf("section: [%s]\r\n", s);
        int count = ini_pair_count(ini, s);
        for (int j = 0; j < count; j++)
        {
            char *k = (const char*)ini_key_name(ini, s, j);
            printf("%s : %s\r\n", k, ini_get_value(ini, s, k));
        }
    }
    #endif
    ini_file_dump(ini, WRITE_FILE);
    ini_delete(ini);
}

static void test_base(void)
{
    // test_dump();
    test_load();
}

/************************************************************************************/
/*************************************  Command  ************************************/
/************************************************************************************/

static void usage(void)
{
    printf(
"Usage: ini [opt] [arg] ...\n"
"\n"
"options:\n"
"    -e <execute>        Specifies the function to execute, the default is the <base> test\n"
"                        <base>      Test base function\n"
"                        <ut>        Unit test\n"
"                        <create>    Test create and delete functions\n"
"                        <opsect>    Test operate section functions\n"
"                        <getsect>   Test get section infomations functions\n"
"                        <oppair>    Test operate pair functions\n"
"                        <getkey>    Test get key infomations functions\n"
"                        <dump>      Test dump functions\n"
"                        <load>      Test load functions\n"
"    -h                  Print help\n"
"    -v                  Print version\n"
"    -u [<period>]       Unit test period, unit ms, the default is 1000ms\n"
"\n"
    );
}

static int test(int argc, char *argv[])
{
    char *execute = NULL;
    int ut_period = 1000;

    /* reset getopt */
    command_opt_init();

    while (1)
    {
        int opt = command_getopt(argc, argv, "e:hvu::");
        if (opt == -1) break;

        switch (opt) 
        {
        case 'u' :
            if (command_optarg) ut_period = atoi(command_optarg);
            break;
        case 'e' :
            execute = command_optarg;
            break;
        case 'v' :
            printf("ini version %d.%d.%d\r\n", INI_V_MAJOR, INI_V_MINOR, INI_V_PATCH);
            return 0;
        case '?':
            printf("Unknown option `%c`\r\n", command_optopt);
            return -1;
        case 'h' : 
        default:
            usage();
            return 0;
        }
    }

    if (execute)
    {
        if (!strcmp(execute, "base"))
        {
            test_base();
        }
        else if (!strcmp(execute, "ut"))
        {
            srand((unsigned int)time(NULL));
            #if defined(TEST_TARGET_ini)
            while (1)
            {
                unitt_task();
                usleep(1000 * ut_period);
            }
            #else  
            printf("create task %d\r\n", task_create(ut_period, unitt_task));
            #endif
        }
        else if (!strcmp(execute, "create"))
        {
            test_create();
        }
        else if (!strcmp(execute, "opsect"))
        {
            test_op_sections();
        }
        else if (!strcmp(execute, "getsect"))
        {
            test_get_sections();
        }
        else if (!strcmp(execute, "oppair"))
        {
            test_op_pairs();
        }
        else if (!strcmp(execute, "getkey"))
        {
            test_get_key();
        }
        else if (!strcmp(execute, "dump"))
        {
            test_dump();
        }
        else if (!strcmp(execute, "load"))
        {
            test_load();
        }
    }
    else  
    {
        test_base();
    }
    
    return 0;
}

/************************************************************************************/
/************************************ Test entry ************************************/
/************************************************************************************/

#if defined(TEST_TARGET_ini)
int main(int argc, char *argv[])
{
    return test(argc, argv);
}
#else 
void test_ini(void)
{
    command_export("ini", test);
}
init_export_app(test_ini);
#endif 
